<?php

namespace qiniu;

use qiniu\rs\PutPolicy;
use yii\base\Component;
use yii\base\Exception;
use yii\base\InvalidConfigException;
use Yii;
use yii\base\InvalidParamException;

class Qiniu extends Component
{
    const LIST_URL = 'http://rsf.qbox.me/list';

    /**
     * @var string 访问key
     */
    public $accessKey;

    /**
     * @var string 访问密匙
     */
    public $secretKey;

    /**
     * @inheritdoc
     *
     * @throws \yii\base\InvalidConfigException
     */
    public function init()
    {
        parent::init();
        if (!isset($this->accessKey, $this->secretKey)) {
            throw new InvalidConfigException();
        }
    }

    /**
     * 生成访问凭证
     *
     * @param $url
     * @param string|null $body
     * @return string
     */
    public function generateAccessToken($url, $body = null)
    {
        $parts = parse_url($url);
        $signingStr = '';
        if (isset($parts['path'])) {
            $signingStr = $parts['path'];
        }
        if (isset($parts['query'])) {
            $signingStr .= '?' . $parts['query'];
        }
        $signingStr .= "\n";
        if ($body) {
            $signingStr .= $body;
        }
        $sign = \hash_hmac('sha1', $signingStr, $this->secretKey, true);
        return $this->accessKey . ':' . Util::encode($sign);
    }

    /**
     * 生成上传凭证
     *
     * @param rs\PutPolicy $putPolicy
     * @throws \yii\base\InvalidParamException
     * @return string
     */
    public function generateUploadToken(PutPolicy $putPolicy)
    {
        $encodePolicy = Util::encode($putPolicy->json());
        $sign = \hash_hmac('sha1', $encodePolicy, $this->secretKey, true);
        return $this->accessKey . ':' . Util::encode($sign) . ':' . $encodePolicy;
    }


    /**
     * 是否是七牛的回调
     *
     * @param string $callbackPath 回调的路径
     * @return bool
     */
    public function isQiuniuCallback($callbackPath)
    {
        $request = Yii::$app->getRequest();
        if (!isset($request->headers['AUTHORIZATION'])) {
            return false;
        }

        $authStr = $request->headers['AUTHORIZATION'];
        if (strpos($authStr, 'QBox ') === false) {
            return false;
        }

        $auth = explode(':', substr($authStr, 5));
        if (count($auth) !== 2 || $auth[0] != $this->accessKey) {
            return false;
        }

        $data = $callbackPath . "\n" . $request->getRawBody();
        return Util::encode(\hash_hmac('sha1', $data, $this->secretKey, true)) == $auth[1];
    }

    /**
     * 请求
     *
     * @param $url
     * @param array $headers
     * @param string|null $body
     * @return mixed
     * @throws \yii\base\Exception
     * @throws InvalidResponseException
     */
    protected function request($url, $headers = [], $body = null)
    {
        $ch = curl_init();
        $curlOptions = [
            CURLINFO_HEADER_OUT => YII_DEBUG,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_CONNECTTIMEOUT => 30,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_URL => $url,
        ];

        if ($body) {
            $curlOptions[CURLOPT_POST] = true;
            $curlOptions[CURLOPT_POSTFIELDS] = $body;
        }

        foreach ($curlOptions as $option => $value) {
            curl_setopt($ch, $option, $value);
        }

        $response = curl_exec($ch);
        $responseHeaders = curl_getinfo($ch);

        $errorNumber = curl_errno($ch);
        $errorMessage = curl_error($ch);

        Yii::trace(curl_getinfo($ch, CURLINFO_HEADER_OUT));

        curl_close($ch);

        if ($errorNumber > 0) {
            throw new Exception('Curl error requesting "' . $url . '":#' . $errorNumber . ' - ' . $errorMessage);
        }

        if (strncmp($responseHeaders['http_code'], '20', 2) !== 0) {
            throw new InvalidResponseException($responseHeaders, $response, 'Request failed with code: ' . $responseHeaders['http_code'] . ', message: ' . $response);
        }

        if ($result = json_decode($response, true)) {
            if (isset($result['code'])) {
                throw new InvalidResponseException($responseHeaders, $response, $result['error'], $result['code']);
            }
            return $result;
        } else {
            throw new Exception('Response decode error :#' . json_last_error() . ' - ' . json_last_error_msg());
        }
    }

    public function apiMkblk()
    {

    }

    public function apiList($bucket, $params = [])
    {
        $url = self::LIST_URL . '?' . http_build_query(array_merge($params, [
            'bucket' => $bucket,
        ]), null, '&');

        return $this->request($url, ['Authorization: QBox ' . $this->generateAccessToken($url)]);
    }
}
